package com.xiangxiao.rpan.storage.factory;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.xiangxiao.rpan.storage.anotion.StorageParamItem;
import com.xiangxiao.rpan.storage.constant.ResponseCode;
import com.xiangxiao.rpan.storage.constant.ResponseMessage;
import com.xiangxiao.rpan.storage.entity.StorageSource;
import com.xiangxiao.rpan.storage.entity.StorageSourceConfig;
import com.xiangxiao.rpan.storage.entity.StorageSourceParamDef;
import com.xiangxiao.rpan.storage.enums.StorageTypeEnum;
import com.xiangxiao.rpan.storage.exception.StorageExcetion;
import com.xiangxiao.rpan.storage.params.IStorageParam;
import com.xiangxiao.rpan.storage.service.AbstractBaseFileService;
import com.xiangxiao.rpan.storage.service.StorageSourceService;
import com.xiangxiao.rpan.utils.ClassUtils;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

/**
 * @auther xiangxiao
 * @email 573768011@qq.com
 * @data 2023/10/19 15:19
 */
@Component
@DependsOn("storageSourceService")
@Slf4j
public class StorageSourceContext implements ApplicationContextAware {
  private static Logger logger = LoggerFactory.getLogger(StorageSourceContext.class);
  /**
   * Map<Integer, AbstractBaseFileService>
   * Map<存储源 ID, 存储源 Service>
   */
  private static final Map<Integer, AbstractBaseFileService<IStorageParam>> DRIVES_SERVICE_MAP = new ConcurrentHashMap<>();
  /**
   * Map<存储源类型, 存储源 Service>
   */
  private static  Map<String, AbstractBaseFileService> storageTypeEnumFileServiceMap;
  /**
   * 缓存每个存储源参数的字段列表.
   */
  private final Map<Class<?>, Map<String, Field>> PARAM_CLASS_FIELD_NAME_MAP_CACHE = new HashMap<>();
  @Resource
  private StorageSourceService storageSourceService;

  private ApplicationContext applicationContext;

  /**
   * 项目启动时, 自动调用数据库已存储的所有存储源进行初始化.
   */
  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    this.applicationContext = applicationContext;
    storageTypeEnumFileServiceMap = this.applicationContext.getBeansOfType(AbstractBaseFileService.class);
  }

  /**
   * 初始化指定存储源的 Service, 添加到上下文环境中.
   * @param   storageSource
   * 存储源对象
   */
  public void init(StorageSource storageSource) {
    Integer storageId = storageSource.getId();
    String storageName = storageSource.getName();

    AbstractBaseFileService<IStorageParam> baseFileService = getInitStorageBeanByStorageId(storageSource.getType());
    if (baseFileService == null) {
      throw new StorageExcetion(ResponseMessage.STORAGE_SOURCE_NOT_FOUND, ResponseCode.STORAGE_SOURCE_NOT_FOUND);
    };

    // 填充初始化参数
    baseFileService.setStorageId(storageId);
    baseFileService.setName(storageName);
    IStorageParam initParam = getInitParam(storageSource.getType().name(), baseFileService);
    baseFileService.setParam(initParam);

    // 进行初始化并测试连接
    baseFileService.init();
//    baseFileService.testConnection();

    DRIVES_SERVICE_MAP.put(storageId, baseFileService);
  }

  /**
   * 根据存储类型获取对应的存储源的参数列表.
   * @param   type
   *          存储类型: {@link StorageTypeEnum}
   * @return  指定类型存储源的参数列表. {@link AbstractBaseFileService#()}
   */
  public static List<StorageSourceParamDef> getStorageSourceParamListByType(StorageTypeEnum type) {
    return storageTypeEnumFileServiceMap.values().stream()
        // 根据存储源类型找到第一个匹配的 Service
        .filter(fileService -> fileService.getStorageTypeEnum() == type)
        .findFirst()
        // 获取该 Service 的参数列表
        .map((Function<AbstractBaseFileService, List<StorageSourceParamDef>>) AbstractBaseFileService::getStorageSourceParamList)
        // 如果没有找到, 则返回空列表
        .orElse(Collections.emptyList());
  }

  /**
   * 根据type获取指定存储源初始状态的 Service.
   * @param   type
   *          存储源的type
   * @return  存储源对应初始化的 Service
   */
  public AbstractBaseFileService getInitStorageBeanByStorageId(StorageTypeEnum type) {
    for (AbstractBaseFileService<?> value : storageTypeEnumFileServiceMap.values()) {
      if (Objects.equals(value.getStorageTypeEnum(), type)) {
        System.out.println(value);
        return value;
      }
    }
    return null;
  }

  /**
   * 获取指定存储源的初始化参数.
   * @param   storageKey
   *          存储源 Key
   * @return  存储源初始化参数
   */
  private IStorageParam getInitParam(String storageKey, AbstractBaseFileService<?> baseFileService) {
    // 获取存储源实现类的实际 Class
    Class<?> beanTargetClass = AopUtils.getTargetClass(baseFileService);
    // 获取存储源实现类的实际 Class 的泛型参数类型
    // 这里获得的是AbstractBaseFileService 实现类Class
    Class<?> paramClass = ClassUtils.getClassFirstGenericsParam(beanTargetClass);

    // 获取存储器参数 key -> 存储器 field 对照关系，如果缓存中有，则从缓存中取.
    Map<String, Field> fieldMap = new HashMap<>();
    if (paramClass != null) {
      if (PARAM_CLASS_FIELD_NAME_MAP_CACHE.containsKey(paramClass)) {
        fieldMap = PARAM_CLASS_FIELD_NAME_MAP_CACHE.get(paramClass);
      } else {
        Field[] fields = ReflectUtil.getFieldsDirectly(paramClass, true);
        for (Field field : fields) {
          String key;

          StorageParamItem storageParamItem = field.getDeclaredAnnotation(StorageParamItem.class);
          // 没有注解或注解中没有配置 key 则使用字段名.
          if (storageParamItem == null || StrUtil.isEmpty(storageParamItem.key())) {
            key = field.getName();
          } else {
            key = storageParamItem.key();
          }

          fieldMap.put(key, field);
        }
        PARAM_CLASS_FIELD_NAME_MAP_CACHE.put(paramClass, fieldMap);
      }

      // 实例化参数对象
      IStorageParam iStorageParam = ReflectUtil.newInstance(paramClass.getName());

      // 给所有字段填充值
      List<StorageSourceConfig> storageSourceConfigList = storageSourceService.selectStorageConfigByStorageKey(storageKey);
      for (StorageSourceConfig storageSourceConfig : storageSourceConfigList) {
        String name = storageSourceConfig.getName();
        String value = storageSourceConfig.getValue();
        try {
          Field field = fieldMap.get(name);
          ReflectUtil.setFieldValue(iStorageParam, field, value);
        } catch (Exception e) {
          String errMsg = StrUtil.format("为字段 {} 初始化值 {} 失败", name, value);
          logger.error(errMsg, e.getMessage());
          throw new StorageExcetion(ResponseMessage.STORAGE_SOURCE_INIT_STORAGE_PARAM_FIELD_FAIL, ResponseCode.STORAGE_SOURCE_INIT_STORAGE_PARAM_FIELD_FAIL);
        }
      }

      return iStorageParam;
    }
    return null;

  }

  /**
   * 根据存储源 id 获取对应的 Service.
   * @param   storageId
   *          存储源 ID
   * @return  存储源对应的 Service
   */
  public AbstractBaseFileService<IStorageParam> getByStorageId(Integer storageId) {
    AbstractBaseFileService<IStorageParam> abstractBaseFileService = DRIVES_SERVICE_MAP.get(storageId);
    if (abstractBaseFileService == null) {
      throw new StorageExcetion("通过存储源 id 未找到存储源, id: " + storageId);
    }
    return abstractBaseFileService;
  }

  /**
   * 销毁指定存储源的 Service.
   *
   * @param   storageId
   *          存储源 ID
   */
  public void destroy(Integer storageId) {
    log.info("清理存储源上下文对象, storageId: {}", storageId);
    DRIVES_SERVICE_MAP.remove(storageId);
    StorageTypeEnum storageTypeEnum = storageSourceService.findStorageTypeById(storageId);
    AbstractBaseFileService<IStorageParam> baseFileService = getInitStorageBeanByStorageId(storageTypeEnum);
    if (baseFileService == null) {
      throw new StorageExcetion(ResponseMessage.STORAGE_SOURCE_NOT_FOUND, ResponseCode.STORAGE_SOURCE_NOT_FOUND);
    };

    // 填充初始化参数
    baseFileService.setStorageId(null);
    baseFileService.setName(null);
    baseFileService.setParam(null);
  }
}
